package org.unidata.mdm.rest.v1.data.service.atomic;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.stream.Collectors;

import org.apache.commons.collections4.MapUtils;
import org.unidata.mdm.data.dto.ErrorInfoDTO;
import org.unidata.mdm.data.dto.RelationStateDTO;
import org.unidata.mdm.data.dto.UpsertRecordDTO;
import org.unidata.mdm.data.dto.UpsertRelationDTO;
import org.unidata.mdm.data.dto.UpsertRelationsDTO;
import org.unidata.mdm.data.type.data.EtalonRecord;
import org.unidata.mdm.rest.system.ro.details.InfoDetailsRO;
import org.unidata.mdm.rest.system.ro.details.ResultDetailsRO;
import org.unidata.mdm.rest.system.service.TypedRestOutputRenderer;
import org.unidata.mdm.rest.v1.data.converter.DataRecordEtalonConverter;
import org.unidata.mdm.rest.v1.data.converter.RelationToEtalonConverter;
import org.unidata.mdm.rest.v1.data.module.DataRestModule;
import org.unidata.mdm.rest.v1.data.ro.atomic.AtomicDataUpsertResultRO;
import org.unidata.mdm.rest.v1.data.ro.records.EtalonRelationToRO;
import org.unidata.mdm.rest.v1.data.ro.records.UpsertRecordResultRO;
import org.unidata.mdm.rest.v1.data.ro.relations.UpsertRelationsResultRO;
import org.unidata.mdm.system.type.rendering.FieldDef;
import org.unidata.mdm.system.type.rendering.FragmentDef;

/**
 * Atomic upsert result renderer
 *
 * @author Alexandr Serov
 * @since 09.11.2020
 **/
public class AtomicDataUpsertResultRenderer extends TypedRestOutputRenderer<AtomicDataUpsertResultRO, UpsertRecordDTO> {

    public static final String DATA_DOMAIN = "DATA";

    public AtomicDataUpsertResultRenderer() {
        super(AtomicDataUpsertResultRO.class, UpsertRecordDTO.class,
            Collections.singletonList(FieldDef.fieldDef(DataRestModule.MODULE_ID, UpsertRecordResultRO.class))
        );
    }

    @Override
    protected Map<String, Object> renderFragmentFields(FragmentDef fragmentDef, UpsertRecordDTO container) {
        return Collections.singletonMap(DataRestModule.MODULE_ID, toUpsertEntityResultRO(container));
    }

    private UpsertRecordResultRO toUpsertEntityResultRO(UpsertRecordDTO upsertResult) {
        UpsertRecordResultRO result = new UpsertRecordResultRO();
        EtalonRecord resultEtalonRecord = upsertResult.getEtalon();
        result.setDetails(errorsToDetails(upsertResult.getErrors()));
        result.setDuplicates(upsertResult.getDuplicateIds());
        if (resultEtalonRecord != null) {
            result.setDataRecord(DataRecordEtalonConverter.to(upsertResult.getEtalon(), upsertResult.getRecordKeys()));
        }
        result.setRelationsResult(renderRelationsResult(upsertResult));
        return result;
    }

    protected ResultDetailsRO errorsToDetails(Collection<ErrorInfoDTO> errors) {
        return updateResultDetails(new ResultDetailsRO(), errors);
    }

    protected ResultDetailsRO updateResultDetails(ResultDetailsRO target, Iterable<ErrorInfoDTO> errors) {
        if (errors != null) {
            for (ErrorInfoDTO error : errors) {
                InfoDetailsRO warn = new InfoDetailsRO();
                warn.setDomain(DATA_DOMAIN);
                warn.setSource(error.getErrorCode());
                warn.setContent(error.getUserMessageDetails());
                target.withWarning(warn);
            }
        }
        return target;
    }

    private UpsertRelationsResultRO renderRelationsResult(UpsertRecordDTO record) {

        UpsertRelationsResultRO result = new UpsertRelationsResultRO();
        UpsertRelationsDTO relations = record.fragment(UpsertRelationsDTO.ID);

        if (Objects.nonNull(relations) && MapUtils.isNotEmpty(relations.getRelations())) {

            List<EtalonRelationToRO> etalonRelations = new ArrayList<>();
            for (Entry<RelationStateDTO, List<UpsertRelationDTO>> er : relations.getRelations().entrySet()) {

                etalonRelations.addAll(er.getValue().stream()
                    .filter(Objects::nonNull)
                    .map(urr -> {

                        EtalonRelationToRO etalonRelation = RelationToEtalonConverter.to(urr.getEtalon());
                        etalonRelation.setDraftId(urr.getDraftId());

                        return etalonRelation;
                    })
                    .collect(Collectors.toList()));
            }

            result.setEtalonRelations(etalonRelations);
        }

        return result;
    }
}
